package cn.funjson.dicttransformer.aspect;

import cn.funjson.dicttransformer.compoment.DictKey;
import cn.funjson.dicttransformer.compoment.DictKeys;
import cn.funjson.dicttransformer.hook.AbstractDataHook;
import cn.funjson.dicttransformer.model.Node;
import cn.funjson.dicttransformer.utils.AopUtils;
import cn.funjson.dicttransformer.utils.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 字典转换核心aop
 *
 * @author fengjiansong
 * @version v3.0
 * @date 2021/8/10 21:58
 */
@Aspect
@Component(value = "DickAspect")
@Slf4j
@Order(3)
@ConditionalOnBean({AbstractDataHook.class,SpringUtil.class})
public class DickAspect {

    /**
     * 只转换response切点
     */
    @Pointcut("@annotation(cn.funjson.dicttransformer.compoment.EnableDict)")
    public void switchPoint() {

    }

    /**
     * request response 可选转换切点
     */
    @Pointcut("@annotation(cn.funjson.dicttransformer.compoment.EnableFullDict)")
    public void switchFullPoint() {

    }

    /**
     * 只转换response切面
     *
     * @param joinPoint joinPoint
     * @return 返回值
     * @throws Throwable 字典转换异常
     */
    @Around("switchPoint()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        return transferResponse(joinPoint);
    }

    /**
     * request response 可选转换切面
     *
     * @param joinPoint joinPoint
     * @return 返回值
     * @throws Throwable 字典转换异常
     */
    @Around("switchFullPoint()")
    public Object aroundFullMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        // TODO 增加对请求体的转换 transferRequest(joinPoint);
        return transferRequest(joinPoint);
    }

    private Object transferRequest(ProceedingJoinPoint joinPoint) {
        return null;
    }

    private Method getTargetMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取类的字节码对象，通过字节码对象获取方法信息
        Class<?> targetCls = joinPoint.getTarget().getClass();
        //获取方法签名(通过此签名获取目标方法信息)
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        //获取目标方法上的注解指定的操作名称
        Method targetMethod =
                targetCls.getDeclaredMethod(
                        ms.getName(),
                        ms.getParameterTypes());
        //获取目标方法名(目标类型+方法名)
        String targetClsName = targetCls.getName();
        String targetObjectMethodName = targetClsName + "." + ms.getName();
        log.debug("[字典转换]:[目标方法]:{}", targetObjectMethodName);
        return targetMethod;
    }

    /**
     * 转换核心逻辑
     *
     * @param joinPoint joinPoint
     * @return Object
     * @throws Throwable 异常
     */
    private Object transferResponse(ProceedingJoinPoint joinPoint) throws Throwable {

        var obj = joinPoint.proceed();

        var nodeList = collectKeyAndValue(obj.getClass(), new ArrayList<>(), new Node(), false);

        log.debug("[dict node list]:{}", JSONObject.toJSONString(nodeList));

        nodeList.forEach(p -> {
            try {
                List<Object> list = new ArrayList<>();
                list.add(obj);
                Node.reverseAndUpdateField(list, p, false);
            } catch (Throwable throwable) {
                log.error("转换异常", throwable);
            }
        });

        return obj;
    }

    /**
     * 收集需要转换的key信息
     *
     * @param clazz  clazz
     * @param node   node
     * @param isList isList
     * @return Node node
     */
    private static List<Node> collectKeyAndValue(Class<?> clazz, List<Node> nodeList, Node node, boolean isList) {

        // 初始化node
        var fields = clazz.getDeclaredFields();
        StringBuilder stringBuilder = new StringBuilder();
        Arrays.stream(fields).forEach(p -> {
            // 基础类型数据key
            if (p.getType().equals(String.class) && p.isAnnotationPresent(DictKey.class) ||
                    p.getType().equals(Integer.class) && p.isAnnotationPresent(DictKey.class) ||
                    p.getType().equals(Long.class) && p.isAnnotationPresent(DictKey.class) ||
                    p.getType().equals(String.class) && p.isAnnotationPresent(DictKeys.class) ||
                    p.getType().equals(Integer.class) && p.isAnnotationPresent(DictKeys.class) ||
                    p.getType().equals(Long.class) && p.isAnnotationPresent(DictKeys.class)
            ) {
                if (p.isAnnotationPresent(DictKeys.class)){
                    var array = p.getAnnotation(DictKeys.class).value();
                    for (DictKey dictKey : array){
                        // 序号
                        var valueName = dictKey.valueName();
                        // dict group
                        var group = dictKey.group();
                        // 属性名
                        var fieldName = p.getName();
                        // hook
                        var hook = dictKey.dynamicHookClass();
                        // spring get bean
                        var dataHook = (AbstractDataHook) SpringUtil.getBean(hook);
                        node.setClazz(clazz);
                        node.setGroup(group);
                        node.setKeyName(fieldName);
                        node.setValueName(valueName);
                        node.setFlagList(isList);
                        // 每次放的时候要是一个新的值 才可以保留之前调用链的信息
                        Node newNode = JSONObject.parseObject(JSONObject.toJSONString(node), Node.class);
                        newNode.setDataHook(dataHook);
                        nodeList.add(newNode);
                    }
                }else{
                    // 序号
                    var valueName = p.getAnnotation(DictKey.class).valueName();
                    // dict group
                    var group = p.getAnnotation(DictKey.class).group();
                    // 属性名
                    var fieldName = p.getName();
                    // hook
                    var hook = p.getAnnotation(DictKey.class).dynamicHookClass();
                    // spring get bean
                    var dataHook = (AbstractDataHook) SpringUtil.getBean(hook);
                    node.setClazz(clazz);
                    node.setGroup(group);
                    node.setKeyName(fieldName);
                    node.setValueName(valueName);
                    node.setFlagList(isList);
                    // 每次放的时候要是一个新的值 才可以保留之前调用链的信息
                    Node newNode = JSONObject.parseObject(JSONObject.toJSONString(node), Node.class);
                    newNode.setDataHook(dataHook);
                    nodeList.add(newNode);
                }
            }
            // List object key
            if (p.getType().equals(List.class) || p.getType().equals(Set.class)) {
                // 如果是List类型，得到其Generic的类型
                Type genericType = p.getGenericType();
                // 如果是泛型参数的类型
                if (genericType instanceof ParameterizedType) {
                    //得到泛型里的class类型对象
                    ParameterizedType pt = (ParameterizedType) genericType;
                    var genericClazz = pt.getActualTypeArguments()[0];
                    try {
                        var innerClass = Class.forName(genericClazz.getTypeName());
                        Node innerNode = new Node();
                        // 取当前节点
                        node.setNext(innerNode);
                        node.setFieldName(p.getName());
                        node.setFlagList(true);
                        innerNode.setPre(node);
                        collectKeyAndValue(innerClass, nodeList, innerNode, true);
                    } catch (ClassNotFoundException e) {
                        log.error("[字典转换异常]:[List泛型类型转换异常]", e);
                    }
                } else {
                    throw new RuntimeException("[字典转换异常]:[List泛型缺失]");
                }
            }
            // consumer object key
            if (!AopUtils.isJavaClass(p.getType())) {
                //得到泛型里的class类型对象
                var innerClass = p.getType();
                // 递归查询 返回list
                log.debug("[用户自定义属性]：{}", p.getType());
                Node innerNode = new Node();
                // 取当前节点
                node.setNext(innerNode);
                node.setFieldName(p.getName());
                innerNode.setPre(node);
                collectKeyAndValue(innerClass, nodeList, innerNode, false);
            }
        });
        return nodeList;
    }

}
